package com.str.aop;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.str.annotation.PrintLog;
import com.str.utils.Constant;
import com.str.vo.ResponseVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 打印日志aop
 *
 * @author hui cairong
 * @date 2021/8/10 10:54
 */
@Aspect
@Component
@Slf4j
public class PrintLogAop {

    @Pointcut("@annotation(com.str.annotation.PrintLog)")
    public void logPointCut() {

    }

    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        // 从请求头中获取token
        String token = request.getHeader("X-Token");
        // 如果获取到打印到日志中
        if (!StringUtils.isEmpty(token)) {
            MDC.put("token", token);
        }
        // 打印请求的URI
        String uri = request.getRequestURI();
        log.info("请求URI：" + uri);

        // 获取注解类
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        PrintLog printLog = method.getAnnotation(PrintLog.class);
        // 获取描述信息
        String desc = printLog.desc();
        // 获取请求参数
        String reqData = "";
        try {
            Object[] args = joinPoint.getArgs();
            // 有请求参数
            if (args.length >= 1) {
                Stream stream = ArrayUtils.isEmpty(args) ? Stream.empty() : Arrays.stream(args);
                List<Object> logArgs = (List<Object>) stream.filter(
                        arg -> (!(arg instanceof HttpServletRequest) && !(arg instanceof HttpServletResponse)))
                        .collect(Collectors.toList());
                if (logArgs != null && logArgs.size() > 0) {
                    reqData = logArgs.get(0).toString();
                    // 请求参数是json格式
                    if (JSON.isValid(reqData)) {
                        if (!method.getName().equals("insertExcel")) {
                            JSONObject reqJson = JSONObject.parseObject(reqData);
                            // 没有请求参数
                            if (reqJson.containsKey("attributeNames")) {
                                reqData = "";
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            log.error("--->获取{}请求参数出错", desc, e);
        }
        log.info("--->{}请求参数：{}", desc, reqData);

        Object proceed = joinPoint.proceed();
        if (proceed == null) {
            return proceed;
        }
        //获取响应数据
        String resData = proceed.toString();
        JSONObject resultJson = null;
        if (JSON.isValidObject(resData)) {
            resultJson = JSONObject.parseObject(resData);
        } else {
            ResponseVO responseVO = (ResponseVO) proceed;
            String json = JSON.toJSONString(responseVO);
            resultJson = JSON.parseObject(json);
        }
        //如果响应数据大于规定值，则移除掉数据，只显示状态码和状态信息
        if (resData.length() > Constant.PRINT_LOG_LENGTH) {
            resultJson.remove("data");
            resData = resultJson.toJSONString();
            log.info("<---{}响应数据大于{}：{}", desc, Constant.PRINT_LOG_LENGTH, resData);
        } else {
            log.info("<---{}响应数据：{}", desc, resData);
        }
        MDC.clear();
        return proceed;
    }
}